package gui;

import data.ApplicationProperties;
import data.UserPreferences;
import java.awt.Dialog.ModalityType;
import java.awt.Window;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JDialog;
import javax.swing.SwingUtilities;
import sound.Track;
import tablature.Tablature;

/**
 * Class: MainWindowFrame.java
 * Description: MainWindowFrame is the GUI that holds the main
 * window GUI. It also contains the main method.
 * @author et
 */
public class MainWindowFrame extends javax.swing.JFrame
{
    private UserPreferences up = UserPreferences.getInstance();

    // Instances of the Setup and Tablature windows.
    private SetupWindow setupWindow = SetupWindow.getInstance();
    private TablatureWindow tabWindow = TablatureWindow.getInstance();

    private Tablature tab = Tablature.getInstance();

    // Two modes, recording and doing a countdown.
    private boolean isRecording;
    private boolean isCounting;


    // The background track (MP3)
    private Track backgroundTrack;

    // How many seconds to count down for.
    private int countdown;

    // What the start time of the recording is.
    private long startTime;

    // A singleton instance of this class.
    private static MainWindowFrame instance;


    /**
     * Returns a singleton instances of this class.
     * @return - instance a singleton instance of this class.
     */
    public static MainWindowFrame getInstance()
    {
        if (instance == null)
        {
            instance = new MainWindowFrame();
        }
        return instance;
    }

    /**
     * Constructor for MainWindowFrame
     */
    private MainWindowFrame() 
    {
        isRecording = false;
        isCounting = false;
        backgroundTrack = new Track("");
        countdown = 0;
        startTime = 0;

        initComponents();

        textAreaPanel.add(tabWindow.getMainComponent());

        this.setVisible(true);
    }



    /**
     * Opens the SetupWindow
     */
    protected void showSetupWindowDialog()
    {
        Window windowAncestor = SwingUtilities.getWindowAncestor(this);
        // create my dialog when I need it
        JDialog dialog = new JDialog(windowAncestor, ApplicationProperties.SETTINGS, ModalityType.APPLICATION_MODAL);

        // add my infoDialog's JPanel into the dialog's contentPane
        dialog.getContentPane().add(setupWindow.getMainComponent());
        dialog.pack();
        dialog.setLocationRelativeTo(null);
        dialog.setVisible(true);

        countdown = setupWindow.getCountdownSeconds();
    }

    /**
     * Opens the ExportWindow
     */
    protected void showExportWindowDialog()
    {
        Window windowAncestor = SwingUtilities.getWindowAncestor(this);
        // create my dialog when I need it
        JDialog dialog = new JDialog(windowAncestor, ApplicationProperties.SETTINGS, ModalityType.APPLICATION_MODAL);

        ExportWindow ew = new ExportWindow(tab.getAllMeasures());

        // add my infoDialog's JPanel into the dialog's contentPane
        dialog.getContentPane().add(ew.getMainComponent());
        dialog.pack();
        dialog.setLocationRelativeTo(null);
        dialog.setVisible(true);
    }



    /**
     * Starts recording
     */
    private void recording()
    {

        setRecording(true);

        tab.start(up.getTempo(), up.hasBackgroundMetronomeSound(), up.getTimeSignature());

        String filename = setupWindow.getBackgroundTrack();

        startTime = System.currentTimeMillis();

        // If we have a background track, call playing track.
        if (!filename.equals(""))
        {
            backgroundTrack.setFilename(filename);
            backgroundTrack.play();

            playingTrack();
        }
        // Else, just play without the track.
        else
        {
            playing();
        }
    }

    /**
     * Stops recording.
     */
    private void stopRecording()
    {
        tab.stop();

        setRecording(false);


        stopButton.setEnabled(false);
        startButton.setEnabled(true);
        editMenu.setEnabled(true);

        backgroundTrack.stop();
    }

    /**
     * We call countdown before we start recording.
     * This counts down an arbitrary number of seconds
     * before the application actually starts recording.
     */
    private void countdown()
    {

        startButton.setEnabled(false);
        stopButton.setEnabled(true);
        editMenu.setEnabled(false);
        
        if (countdown > 0)
        {
            setCounting(true);

            final Timer timer = new Timer();
            timer.scheduleAtFixedRate(new TimerTask()
            {
                public void run()
                {
                    updateTimer(countdown * 1000);
                    countdown--;
                    if (countdown < 0)
                    {
                        timer.cancel();
                        setCounting(false);
                        recording();
                    }
                }
            }, 0, 1000);

        }
        else
        {
            recording();
        }
    }


    /**
     * This is playing without a background track.
     */
    private void playing()
    {
        new Thread()
        {
            public void run()
            {
                try
                {
                    updateTimer(System.currentTimeMillis() - startTime);

                    if (isRecording())
                    {
                        playing();
                    }
                    else
                    {
                        stopRecording();
                    }

                }
                catch (Exception e)
                {
                    System.out.println(e);
                }
            }
        }.start();
    }

    /**
     * This is playing with a background track.
     */
    private void playingTrack()
    {
        new Thread()
        {
            public void run()
            {
                try
                {
                    updateTimer(System.currentTimeMillis() - startTime);
                    
                    if (!backgroundTrack.done() && isRecording())
                    {
                        playingTrack();
                    }


                }
                catch (Exception e)
                {
                    System.out.println(e);
                }
            }
        }.start();
    }

    /**
     * Returns whether or not we are recording.
     * @return isRecording - True if we are recording, false otherwise.
     */
    private synchronized boolean isRecording()
    {
        return isRecording;
    }

    /**
     * Sets whether or not we are recording.
     * @param isRecording - sets whether we are recording or not.
     */
    private synchronized void setRecording(boolean isRecording)
    {
        this.isRecording = isRecording;
    }

    /**
     * Returns whether or not we are counting down.
     * @return isCounting - True if we are counting down, false otherwise.
     */
    private synchronized boolean isCounting()
    {
        return isCounting;
    }

    /**
     * Sets whether or not we are counting down.
     * @param isCounting - sets whether we are counting down or not.
     */
    private synchronized void setCounting(boolean isCounting)
    {
        this.isCounting = isCounting;
    }


    /**
     * Updates the timer with a clock representation of the current playing time.
     * @param milliseconds - the current playing time in milliseconds.
     */
    private void updateTimer(long milliseconds)
    {
        long timeMillis = milliseconds;
        long time = timeMillis / 1000;
        String seconds = Integer.toString((int)(time % 60));
        String minutes = Integer.toString((int)((time % 3600) / 60));
        String hours = Integer.toString((int)(time / 3600));
        for (int i = 0; i < 2; i++)
        {
            if (seconds.length() < 2)
            {
                seconds = "0" + seconds;
            }
            if (minutes.length() < 2)
            {
               minutes = "0" + minutes;
            }
            if (hours.length() < 2)
            {
               hours = "0" + hours;
            }
        }
        timerLabel.setText(hours + ":" + minutes + ":" + seconds);
    }


    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        quickAccessPanel = new javax.swing.JPanel();
        buttonPanel = new javax.swing.JPanel();
        startButton = new javax.swing.JButton();
        stopButton = new javax.swing.JButton();
        playerPanel = new javax.swing.JPanel();
        timerLabel = new javax.swing.JLabel();
        textAreaPanel = new javax.swing.JPanel();
        menuBar = new javax.swing.JMenuBar();
        fileMenu = new javax.swing.JMenu();
        exportMenuItem = new javax.swing.JMenuItem();
        separator = new javax.swing.JSeparator();
        quitMenuItem = new javax.swing.JMenuItem();
        editMenu = new javax.swing.JMenu();
        settingsMenuItem = new javax.swing.JMenuItem();
        helpMenu = new javax.swing.JMenu();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle(ApplicationProperties.DRUM_TABBER_NAME);
        setResizable(false);

        startButton.setFont(new java.awt.Font("Bitstream Vera Sans", 1, 12));
        startButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/img/play.png"))); // NOI18N
        startButton.setText("Start");
        startButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/img/play-disabled.png"))); // NOI18N
        startButton.setFocusPainted(false);
        startButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                startButtonActionPerformed(evt);
            }
        });

        stopButton.setFont(new java.awt.Font("Bitstream Vera Sans", 1, 12));
        stopButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/img/stop.png"))); // NOI18N
        stopButton.setText("Stop");
        stopButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/img/stop-disabled.png"))); // NOI18N
        stopButton.setEnabled(false);
        stopButton.setFocusPainted(false);
        stopButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                stopButtonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout buttonPanelLayout = new javax.swing.GroupLayout(buttonPanel);
        buttonPanel.setLayout(buttonPanelLayout);
        buttonPanelLayout.setHorizontalGroup(
            buttonPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(buttonPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(buttonPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(startButton, javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(stopButton, javax.swing.GroupLayout.DEFAULT_SIZE, 106, Short.MAX_VALUE))
                .addContainerGap())
        );
        buttonPanelLayout.setVerticalGroup(
            buttonPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(buttonPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(startButton)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(stopButton)
                .addContainerGap(14, Short.MAX_VALUE))
        );

        timerLabel.setFont(new java.awt.Font("Dialog", 1, 18));
        timerLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        timerLabel.setText("00:00:00");

        javax.swing.GroupLayout playerPanelLayout = new javax.swing.GroupLayout(playerPanel);
        playerPanel.setLayout(playerPanelLayout);
        playerPanelLayout.setHorizontalGroup(
            playerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(playerPanelLayout.createSequentialGroup()
                .addGap(126, 126, 126)
                .addComponent(timerLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 109, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(145, Short.MAX_VALUE))
        );
        playerPanelLayout.setVerticalGroup(
            playerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(playerPanelLayout.createSequentialGroup()
                .addGap(18, 18, 18)
                .addComponent(timerLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 38, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(60, Short.MAX_VALUE))
        );

        javax.swing.GroupLayout quickAccessPanelLayout = new javax.swing.GroupLayout(quickAccessPanel);
        quickAccessPanel.setLayout(quickAccessPanelLayout);
        quickAccessPanelLayout.setHorizontalGroup(
            quickAccessPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(quickAccessPanelLayout.createSequentialGroup()
                .addComponent(buttonPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(playerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        quickAccessPanelLayout.setVerticalGroup(
            quickAccessPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(buttonPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addComponent(playerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        );

        textAreaPanel.setLayout(new java.awt.BorderLayout());

        fileMenu.setText("File");

        exportMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_E, java.awt.event.InputEvent.CTRL_MASK));
        exportMenuItem.setFont(new java.awt.Font("Bitstream Vera Sans", 1, 12)); // NOI18N
        exportMenuItem.setIcon(new javax.swing.ImageIcon(getClass().getResource("/img/export.png"))); // NOI18N
        exportMenuItem.setText("Export");
        exportMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                exportMenuItemActionPerformed(evt);
            }
        });
        fileMenu.add(exportMenuItem);
        fileMenu.add(separator);

        quitMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Q, java.awt.event.InputEvent.CTRL_MASK));
        quitMenuItem.setFont(new java.awt.Font("Bitstream Vera Sans", 1, 12));
        quitMenuItem.setIcon(new javax.swing.ImageIcon(getClass().getResource("/img/quit.png"))); // NOI18N
        quitMenuItem.setText("Quit");
        quitMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                quitMenuItemActionPerformed(evt);
            }
        });
        fileMenu.add(quitMenuItem);

        menuBar.add(fileMenu);

        editMenu.setText("Edit");

        settingsMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.CTRL_MASK));
        settingsMenuItem.setFont(new java.awt.Font("Bitstream Vera Sans", 1, 12));
        settingsMenuItem.setIcon(new javax.swing.ImageIcon(getClass().getResource("/img/settings.png"))); // NOI18N
        settingsMenuItem.setText("Settings");
        settingsMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                settingsMenuItemActionPerformed(evt);
            }
        });
        editMenu.add(settingsMenuItem);

        menuBar.add(editMenu);

        helpMenu.setText("Help");
        menuBar.add(helpMenu);

        setJMenuBar(menuBar);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(quickAccessPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addComponent(textAreaPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 516, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addComponent(quickAccessPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(textAreaPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 617, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void settingsMenuItemActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_settingsMenuItemActionPerformed
    {//GEN-HEADEREND:event_settingsMenuItemActionPerformed
        showSetupWindowDialog();
    }//GEN-LAST:event_settingsMenuItemActionPerformed

    private void startButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_startButtonActionPerformed
    {//GEN-HEADEREND:event_startButtonActionPerformed
        countdown();
}//GEN-LAST:event_startButtonActionPerformed

    private void stopButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_stopButtonActionPerformed
    {//GEN-HEADEREND:event_stopButtonActionPerformed
        stopRecording();
}//GEN-LAST:event_stopButtonActionPerformed

    private void quitMenuItemActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_quitMenuItemActionPerformed
    {//GEN-HEADEREND:event_quitMenuItemActionPerformed
        System.exit(0);
}//GEN-LAST:event_quitMenuItemActionPerformed

    private void exportMenuItemActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_exportMenuItemActionPerformed
    {//GEN-HEADEREND:event_exportMenuItemActionPerformed
        showExportWindowDialog();
    }//GEN-LAST:event_exportMenuItemActionPerformed

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new MainWindowFrame().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JPanel buttonPanel;
    private javax.swing.JMenu editMenu;
    private javax.swing.JMenuItem exportMenuItem;
    private javax.swing.JMenu fileMenu;
    private javax.swing.JMenu helpMenu;
    private javax.swing.JMenuBar menuBar;
    private javax.swing.JPanel playerPanel;
    private javax.swing.JPanel quickAccessPanel;
    private javax.swing.JMenuItem quitMenuItem;
    private javax.swing.JSeparator separator;
    private javax.swing.JMenuItem settingsMenuItem;
    private javax.swing.JButton startButton;
    private javax.swing.JButton stopButton;
    private javax.swing.JPanel textAreaPanel;
    private javax.swing.JLabel timerLabel;
    // End of variables declaration//GEN-END:variables


}
